package chess

import (
	"fmt"
	"strings"

	"gddgame.cc/galaxy/asteroid"
	"gddgame.cc/galaxy/asteroid/cluster"
	"gddgame.cc/galaxy/binocle"
	"gddgame.cc/galaxy/common/feature/orbit"
	"gddgame.cc/galaxy/project/orbit/orbit/common"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
)

type BaseLayerInstance struct {
	*BlockInstance
	*ClientComponent

	//cluster.Socket
}

func (layer *BaseLayerInstance) NeedForward() bool {
	return true
}

type LogicLayerInstance struct {
	*BlockInstance
	*ClientComponent
}

func (layer *LogicLayerInstance) NeedForward() bool {
	return false
}

type LayerService struct {
	def.Logger
	binocle.Service

	Block *BlockService
}

func (service *LayerService) ID() string {
	return "chessLayer"
}
func (service *LayerService) Master(node cluster.Node) {

}
func (service *LayerService) Init(node cluster.Node) {
	node.SetConfig(cluster.ThreadSafe, true)

	token := node.Token()
	blockIndex := strings.Index(token, "/")
	mapId := token[0:blockIndex]
	blockIndexWithLevel := token[blockIndex+1:]
	levelIndex := strings.LastIndex(blockIndexWithLevel, ":")
	block := orbit.LocationCM.NodeBlock(blockIndexWithLevel[:levelIndex])
	level := utils.ToInt16(utils.ToString(blockIndexWithLevel[levelIndex+1:]))

	//fmt.Println("init", mapId, block, levelIndex, level, blockIndexWithLevel[levelIndex+1:], utils.ToInt16(blockIndexWithLevel[levelIndex+1:]))
	blockInstance := &BlockInstance{
		block:    block,
		Datas:    map[byte]*DataManager{},
		Location: block.ToNode(),
	}
	clientComponent := &ClientComponent{
		Node:       node,
		Block:      block,
		MapId:      mapId,
		levelPoint: orbit.Point(level),
		level:      uint(level),
		service:    service,
	}
	if level == DefaultLevel {
		instance := &BaseLayerInstance{
			BlockInstance:   blockInstance,
			ClientComponent: clientComponent,
		}
		//instance.Socket = cluster.NewSocket(token, instance)

		node.SetInstance(instance)
		service.Block.InitService(node, instance.BlockInstance, instance)
		// 连接到control服务
		// 连接到上层逻辑服务：根据配置确定频次字段
		go func() {
			controlClient := common.NewControlClient(node.Container(), "chess")
			controlClient.Join(instance, mapId)
		}()
	} else {
		instance := &LogicLayerInstance{
			BlockInstance:   blockInstance,
			ClientComponent: clientComponent,
		}
		node.SetInstance(instance)
		service.Block.InitService(node, instance.BlockInstance, instance)
	}
}

func (service *LayerService) InitLogicClient(client *cluster.Client) {

	client.Filter(asteroid.DefaultFilter, func(message asteroid.Message, member def.Member) {
		//fmt.Println("filter")

	})
}

func (service *LayerService) InitClient(client *cluster.Client) {
	token := client.ID()

	blockIndex := strings.Index(token, "/")
	blockIndexWithLevel := token[blockIndex+1:]
	levelIndex := strings.LastIndex(blockIndexWithLevel, ":")
	level := orbit.Point(utils.ToInt16(utils.ToString(blockIndexWithLevel[levelIndex+1:])))

	service.Block.InitClient(client)
	if level > 0 {
		service.InitLogicClient(client)
		return
	}
}

func (service *LayerService) CloseInstance(node cluster.Node) {

}

func (service *LayerService) CloseClient(client *cluster.Client) {

}
func (service *LayerService) GetNodeId(mapId string, sub string, level orbit.Point) string {
	//fmt.Println(utils.ToString(int16(level)), utils.ToInt16(utils.ToString(int16(level))))
	return mapId + "/" + sub + ":" + utils.ToString(int16(level))
}
func (service *LayerService) GetWithSub(container cluster.Container, mapId string, sub string, level orbit.Point) *cluster.Client {
	module := service.ID()
	//if strings.Index(sub, ":") > 0 {
	//	debug.PrintStack()
	//}
	token := service.GetNodeId(mapId, sub, level)
	//fmt.Println("sub", sub, level, token)
	if container == nil {
		fmt.Println("block container empty")
	} else if container.Balance() == nil {
		fmt.Println("block balance empty")
	}
	return container.Balance().GenerateClient(module, token)
}

func (service *LayerService) GetSub(size orbit.Location, position []byte, log bool) string {
	//fmt.Println(position)
	location := orbit.LocationCM.DecodeLocation(position)
	//fmt.Println(location)
	block := orbit.LocationCM.CenterBlockWithLocation(size, size, location)
	if log {
		fmt.Println("block sub", size, position, block)
	}
	sub := orbit.GetSubWithBlock(block)
	orbit.LocationCM.ReleaseLocation(location)
	orbit.LocationCM.ReleaseBlock(block)
	return sub
}
